#include "memorypool.h"
#include "../include/poolalloc.h"
#include <stdlib.h>
#include <string.h>

//! Windows动态库和可执行程序使用不同的堆分配内存，若内存的分配和释放在不同堆中，程序会报错
#if defined(WIN32) || defined(WIN64) || defined(WINCE)
#include <Windows.h>
#define MEMALLOC(len) HeapAlloc(GetProcessHeap(), 0, len)
#define MEMFREE(pobj) HeapFree(GetProcessHeap(), 0, pobj)
#else
#define MEMALLOC(len) malloc(len)
#define MEMFREE(pobj) free(pobj)
#endif

CMemoryPool::CMemoryPool()
{
	m_pMemoryUnit = NULL;
	memset(m_pMemoryChunks, 0, sizeof(m_pMemoryChunks));
}


CMemoryPool::~CMemoryPool()
{
	while(m_pMemoryUnit)
	{
		void* buf = m_pMemoryUnit;
		m_pMemoryUnit = m_pMemoryUnit->next;
		MEMFREE(buf);
	}
}

void* CMemoryPool::alloc_from_chunk(unsigned int len)
{
	if (len > 128)
	{
		return MEMALLOC(len);
	}
	int nIndex = CHUNKINDEX(len);
	MemoryChunk** ppChunk = &(m_pMemoryChunks[nIndex]);
	if (*ppChunk == NULL)
	{
		refillMemoryChunk(ppChunk, ROUND_UP(len));
	}

	void* pRet = *ppChunk;
	*ppChunk = (*ppChunk)->next_free_chunk;
	return pRet;
}

void CMemoryPool::dealloc_to_chunk(void* buf, unsigned int len)
{
	if (len > 128)
	{
		MEMFREE(buf);
		return;
	}

	MemoryChunk* pChunk = (MemoryChunk* )buf;
	memset(buf, 0, len);
	pChunk->next_free_chunk = m_pMemoryChunks[CHUNKINDEX(len)];
	m_pMemoryChunks[CHUNKINDEX(len)] = pChunk;
}

int CMemoryPool::refillMemoryChunk(MemoryChunk** ppChunk, unsigned int len)
{
	int nSize = 20;
	MemoryPoolUnit* pCurUnit = m_pMemoryUnit;
	while(pCurUnit)
	{
		if (pCurUnit->next == NULL)
		{
			break;
		}
		pCurUnit = pCurUnit->next;
	}

	if (pCurUnit == NULL || pCurUnit->len - pCurUnit->cur < CHUNKINDEX(len) + 1)
	{
		MemoryPoolUnit* pNewUnit = (MemoryPoolUnit* )MEMALLOC(sizeof(MemoryPoolUnit) + PAGELEN * POOLUNITLEN);
		memset(pNewUnit, 0, sizeof(MemoryPoolUnit) + PAGELEN * POOLUNITLEN);
		pNewUnit->cur = 0;
		pNewUnit->len = POOLUNITLEN;
		pNewUnit->prev = NULL;
		pNewUnit->next = NULL;

		if (pCurUnit == NULL)
		{
			pCurUnit = pNewUnit;
		}
		else
		{
			pNewUnit->prev = pCurUnit;
			pCurUnit->next = pNewUnit;
			pCurUnit = pNewUnit;
		}
		if (m_pMemoryUnit == NULL)
		{
			m_pMemoryUnit = pNewUnit;
		}
	}

	int nUnused = (pCurUnit->len - pCurUnit->cur) * PAGELEN / ROUND_UP(len);
	nSize = nSize > nUnused ? nUnused : nSize;

	MemoryChunk* pChunk = NULL;
	for(int i = 0; i < nSize; ++i)
	{
		pChunk = (MemoryChunk* )((char* )pCurUnit + sizeof(MemoryPoolUnit) + pCurUnit->cur * PAGELEN);
		memset(pChunk, 0, ROUND_UP(len));
		pChunk->next_free_chunk = *ppChunk;
		*ppChunk = pChunk;
		pCurUnit->cur += (ROUND_UP(len) / PAGELEN);
	}

	return 0;
}

CMemoryPool mempool;
void* pool_allocate(unsigned int len)
{
	return mempool.alloc_from_chunk(len);
}

void pool_deallocate(void* buf, unsigned int len)
{
	mempool.dealloc_to_chunk(buf, len);
}